#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
工具函数模块
包含数据验证、文件操作、显示等通用功能
"""

import os
import re
import logging
from typing import List, Dict, Any, Optional, Union, Callable
from functools import wraps
from datetime import datetime
import json


def setup_logging(log_file: str = "system.log", level: int = logging.INFO) -> logging.Logger:
    """设置日志记录"""
    logger = logging.getLogger('student_management')
    logger.setLevel(level)
    
    # 避免重复添加处理器
    if not logger.handlers:
        # 文件处理器
        file_handler = logging.FileHandler(log_file, encoding='utf-8')
        file_handler.setLevel(level)
        
        # 控制台处理器
        console_handler = logging.StreamHandler()
        console_handler.setLevel(logging.WARNING)
        
        # 格式化器
        formatter = logging.Formatter(
            '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        )
        file_handler.setFormatter(formatter)
        console_handler.setFormatter(formatter)
        
        logger.addHandler(file_handler)
        logger.addHandler(console_handler)
    
    return logger


def validate_student_id(student_id: str) -> bool:
    """验证学号格式"""
    return 6 <= len(student_id) <= 10 and student_id.isdigit()


def validate_name(name: str) -> bool:
    """验证姓名格式"""
    return 2 <= len(name) <= 10 and all('\u4e00' <= char <= '\u9fff' for char in name)


def validate_age(age: int) -> bool:
    """验证年龄范围"""
    return 16 <= age <= 50


def validate_major(major: str) -> bool:
    """验证专业格式"""
    return 2 <= len(major) <= 20


def validate_class_name(class_name: str) -> bool:
    """验证班级格式"""
    return 3 <= len(class_name) <= 15


def validate_email(email: str) -> bool:
    """验证邮箱格式"""
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    return re.match(pattern, email) is not None


def validate_phone(phone: str) -> bool:
    """验证手机号格式"""
    pattern = r'^1[3-9]\d{9}$'
    return re.match(pattern, phone) is not None


class DataValidator:
    """数据验证器"""
    
    @staticmethod
    def validate_student_data(data: Dict[str, Any]) -> List[str]:
        """验证学生数据"""
        errors = []
        
        if not validate_student_id(data.get('student_id', '')):
            errors.append("学号格式错误")
        
        if not validate_name(data.get('name', '')):
            errors.append("姓名格式错误")
        
        if not validate_age(data.get('age', 0)):
            errors.append("年龄范围错误")
        
        if not validate_major(data.get('major', '')):
            errors.append("专业格式错误")
        
        if not validate_class_name(data.get('class_name', '')):
            errors.append("班级格式错误")
        
        return errors
    
    @staticmethod
    def validate_course_data(data: Dict[str, Any]) -> List[str]:
        """验证课程数据"""
        errors = []
        
        if not data.get('course_id'):
            errors.append("课程代码不能为空")
        
        if not data.get('course_name'):
            errors.append("课程名称不能为空")
        
        if not isinstance(data.get('credits'), (int, float)) or data.get('credits', 0) <= 0:
            errors.append("学分必须大于0")
        
        return errors
    
    @staticmethod
    def validate_grade_data(data: Dict[str, Any]) -> List[str]:
        """验证成绩数据"""
        errors = []
        
        if not data.get('student_id'):
            errors.append("学号不能为空")
        
        if not data.get('course_id'):
            errors.append("课程代码不能为空")
        
        score = data.get('score', 0)
        if not isinstance(score, (int, float)) or not (0 <= score <= 100):
            errors.append("成绩必须在0-100之间")
        
        if not data.get('semester'):
            errors.append("学期不能为空")
        
        return errors


class FileManager:
    """文件管理器"""
    
    @staticmethod
    def ensure_directory(directory: str) -> None:
        """确保目录存在"""
        if not os.path.exists(directory):
            os.makedirs(directory)
    
    @staticmethod
    def safe_read_file(file_path: str, encoding: str = 'utf-8') -> List[str]:
        """安全读取文件"""
        try:
            if not os.path.exists(file_path):
                return []
            
            with open(file_path, 'r', encoding=encoding) as f:
                return [line.strip() for line in f if line.strip()]
        except Exception as e:
            logging.error(f"读取文件失败 {file_path}: {e}")
            return []
    
    @staticmethod
    def safe_write_file(file_path: str, data: List[str], encoding: str = 'utf-8') -> bool:
        """安全写入文件"""
        try:
            # 创建备份
            if os.path.exists(file_path):
                backup_path = f"{file_path}.backup"
                os.rename(file_path, backup_path)
            
            with open(file_path, 'w', encoding=encoding) as f:
                for line in data:
                    f.write(line + '\n')
            
            # 删除备份
            backup_path = f"{file_path}.backup"
            if os.path.exists(backup_path):
                os.remove(backup_path)
            
            return True
        except Exception as e:
            logging.error(f"写入文件失败 {file_path}: {e}")
            # 恢复备份
            backup_path = f"{file_path}.backup"
            if os.path.exists(backup_path):
                os.rename(backup_path, file_path)
            return False
    
    @staticmethod
    def save_json(file_path: str, data: Any) -> bool:
        """保存JSON文件"""
        try:
            with open(file_path, 'w', encoding='utf-8') as f:
                json.dump(data, f, ensure_ascii=False, indent=2, default=str)
            return True
        except Exception as e:
            logging.error(f"保存JSON文件失败 {file_path}: {e}")
            return False
    
    @staticmethod
    def load_json(file_path: str) -> Optional[Any]:
        """加载JSON文件"""
        try:
            if not os.path.exists(file_path):
                return None
            
            with open(file_path, 'r', encoding='utf-8') as f:
                return json.load(f)
        except Exception as e:
            logging.error(f"加载JSON文件失败 {file_path}: {e}")
            return None


class DisplayHelper:
    """显示辅助类"""
    
    @staticmethod
    def create_table(data: List[Dict[str, Any]], headers: List[str]) -> str:
        """创建表格字符串"""
        if not data:
            return "暂无数据"
        
        # 计算列宽
        col_widths = {}
        for header in headers:
            col_widths[header] = len(header)
        
        for row in data:
            for header in headers:
                value = str(row.get(header, ''))
                col_widths[header] = max(col_widths[header], len(value))
        
        # 创建表格
        lines = []
        
        # 表头
        header_line = "| " + " | ".join(f"{header:<{col_widths[header]}}" for header in headers) + " |"
        separator = "+" + "+".join("-" * (col_widths[header] + 2) for header in headers) + "+"
        
        lines.append(separator)
        lines.append(header_line)
        lines.append(separator)
        
        # 数据行
        for row in data:
            data_line = "| " + " | ".join(f"{str(row.get(header, '')):<{col_widths[header]}}" for header in headers) + " |"
            lines.append(data_line)
        
        lines.append(separator)
        lines.append(f"共 {len(data)} 条记录")
        
        return "\n".join(lines)
    
    @staticmethod
    def format_menu(title: str, options: List[str], current_user: Optional[str] = None) -> str:
        """格式化菜单"""
        lines = []
        lines.append("=" * 60)
        lines.append(f"           {title}")
        if current_user:
            lines.append(f"当前用户: {current_user}")
        lines.append("=" * 60)
        
        for i, option in enumerate(options, 1):
            lines.append(f"{i}. {option}")
        
        lines.append("=" * 60)
        return "\n".join(lines)
    
    @staticmethod
    def format_error(message: str) -> str:
        """格式化错误信息"""
        return f"❌ 错误: {message}"
    
    @staticmethod
    def format_success(message: str) -> str:
        """格式化成功信息"""
        return f"✅ 成功: {message}"
    
    @staticmethod
    def format_warning(message: str) -> str:
        """格式化警告信息"""
        return f"⚠️ 警告: {message}"


class InputHelper:
    """输入辅助类"""
    
    @staticmethod
    def get_input(prompt: str, validator: Optional[Callable] = None, error_msg: str = "输入格式错误") -> str:
        """获取用户输入并进行验证"""
        while True:
            try:
                value = input(prompt).strip()
                if validator and not validator(value):
                    print(f"❌ {error_msg}")
                    continue
                return value
            except KeyboardInterrupt:
                print("\n操作已取消")
                return ""
            except Exception as e:
                print(f"❌ 输入错误: {e}")
                continue
    
    @staticmethod
    def get_choice(prompt: str, valid_choices: List[str]) -> str:
        """获取用户选择"""
        while True:
            choice = input(prompt).strip()
            if choice in valid_choices:
                return choice
            print(f"❌ 无效选择，请输入: {', '.join(valid_choices)}")
    
    @staticmethod
    def confirm(prompt: str) -> bool:
        """确认操作"""
        while True:
            choice = input(f"{prompt} (y/n): ").strip().lower()
            if choice in ['y', 'yes', '是']:
                return True
            elif choice in ['n', 'no', '否']:
                return False
            print("请输入 y 或 n")


def retry_on_failure(max_retries: int = 3, delay: float = 1.0):
    """重试装饰器"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(max_retries):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if attempt == max_retries - 1:
                        raise e
                    logging.warning(f"操作失败，第 {attempt + 1} 次重试: {e}")
                    import time
                    time.sleep(delay)
            return None
        return wrapper
    return decorator


def cache_result(ttl: int = 300):
    """缓存结果装饰器（简单实现）"""
    def decorator(func):
        cache = {}
        
        @wraps(func)
        def wrapper(*args, **kwargs):
            key = str(args) + str(kwargs)
            now = datetime.now().timestamp()
            
            if key in cache:
                result, timestamp = cache[key]
                if now - timestamp < ttl:
                    return result
            
            result = func(*args, **kwargs)
            cache[key] = (result, now)
            return result
        
        return wrapper
    return decorator


class PerformanceMonitor:
    """性能监控器"""
    
    def __init__(self):
        self.metrics = {}
    
    def start_timer(self, operation: str):
        """开始计时"""
        self.metrics[operation] = {'start': datetime.now()}
    
    def end_timer(self, operation: str):
        """结束计时"""
        if operation in self.metrics:
            duration = (datetime.now() - self.metrics[operation]['start']).total_seconds()
            self.metrics[operation]['duration'] = duration
            logging.info(f"操作 {operation} 耗时: {duration:.2f} 秒")
    
    def get_metrics(self) -> Dict[str, Any]:
        """获取性能指标"""
        return self.metrics.copy()


# 全局实例
logger = setup_logging()
validator = DataValidator()
file_manager = FileManager()
display_helper = DisplayHelper()
input_helper = InputHelper()
performance_monitor = PerformanceMonitor()
